/*
 * Copyright 2013 Agustina Martinez-Garcia

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
 */
package uk.ljmu.qudexapps.xmllib.validators;

import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import java.util.List;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;

import uk.da.dext.exceptions.QudexException;
import uk.ljmu.qudexapps.xmllib.services.QudexFedoraObjects;
import uk.ljmu.qudexapps.xmllib.utilities.Constants;

public class QudexCollectionValidatorImpl implements QudexFoxmlValidator{
	
	private Elements collectionValidator;
	private static Logger log = Logger.getLogger(QudexCollectionValidatorImpl.class);

	/**
	 * @param collectionValidator the collectionValidator to set
	 */
	public void setCollectionValidator(Elements collectionValidator) {
		this.collectionValidator = collectionValidator;
	}

	/**
	 * @return the collectionValidator
	 */
	public Elements getCollectionValidator() {
		return collectionValidator;
	}
	
	public QudexCollectionValidatorImpl() throws QudexException, IOException {
		//Load collection-validator from XML
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		InputStream is = classLoader.getResourceAsStream("validators/collection-validator.xml");
		try {			
			JAXBContext context;
			context = JAXBContext.newInstance(Elements.class);
			Unmarshaller unmarshaller = context.createUnmarshaller();
			//TODO validate against schema, note: setting schema to null will turn validator offs
			unmarshaller.setSchema(null);
			this.collectionValidator = Elements.class.cast(unmarshaller.unmarshal(is));
		} catch (JAXBException e) {
			log.fatal(e.getMessage());
			throw new QudexException("Qudex validator constructor: " + e.getMessage());
		}
		finally {
			if (is != null)
				is.close();
		}
	}
	public boolean validateQudexElements(String xmlContents, List<String> valNs, QudexFedoraObjects qudexObject) {
		Document doc;
		boolean elemPresent;
		try {
			doc = DocumentHelper.parseText(xmlContents);
			Element root = doc.getRootElement();
			Iterator<?> it = root.elementIterator();
			if (!it.hasNext())
				return false;
			while (it.hasNext()) {
				Element elem = (Element) it.next();
				elemPresent = elementPresentSchema(elem.getName(), elem.getNamespacePrefix());
				if (elemPresent) {
					boolean ret = validateElement(elem.getName(), elem.getNamespacePrefix(), root, qudexObject);
					if (!ret)
						return false;
				} else return false;
			}

			return true;
		} catch (DocumentException e) {
			log .fatal(e.getMessage());
		}
		return false;
	}
	
	private boolean elementPresentSchema(String name, String ns) {
		for (uk.ljmu.qudexapps.xmllib.validators.Element elem : this.getCollectionValidator().getElement())  {
			if (elem.getName().equalsIgnoreCase(name) && elem.getNs().equalsIgnoreCase(ns))
				return true;
		}
		return false;
	}

	private boolean validateElement(String name, String ns, Element rootElement, QudexFedoraObjects qudexObject) {
		//Check valid elements present
		boolean found = false;
		for (uk.ljmu.qudexapps.xmllib.validators.Element elem : this.getCollectionValidator().getElement()) {
			if (elem.getName().equalsIgnoreCase(name) && elem.getNs().equalsIgnoreCase(ns)) {
				int num = elem.getMultiplicity().equals(Constants.VALIDATOR_MULTIPLICITY) ? 2 : Integer.parseInt(elem.getMultiplicity());
				List<?> elemsList = rootElement.elements(elem.getName());
				//Mandatory element more than once
				if (!elem.isOptional()) {
					if (elemsList.size() == 0) {
						qudexObject.addFedoraResultsMessage("", 
								"The element <b>" + elem.getName() + "</b> is compulsory. Please add the element to the spreadsheet.");
						return false;
					}
					// TODO Remove this hard-coded value
					if (num == 1 && elemsList.size() > 1 && !elem.getName().equalsIgnoreCase("type")) {
						qudexObject.addFedoraResultsMessage("", 
								"The element <b>" + elem.getName() + "</b> can't occur more than once. Single value field.");
						return false;
					}
				} else {
					if (num == 1 && elemsList.size() != 1) {
						qudexObject.addFedoraResultsMessage("", 
								"The element <b>" + elem.getName() + "</b> can't occur more than one. Single value field.");
						return false;
					}
				}
				found = true;
				break;
			}
		}
		if (found)
			return true;
		else return false;
	}
	
	public void cleanValidator() {
		this.collectionValidator.getElement().clear();
	}

	public List<String> addNamespaceToList(List<String> namespacesList,
		String ns) {
	    // TODO Auto-generated method stub
	    return null;
	}
}
